<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
 * Asset Manager
 *
 * Software to automatically manage how assets are delivered on a website
 *
 * @package		Asset Manager
 * @author		Adam Lewis <adam.lewis@bestservedcold.com>
 * @copyright	Copyright (c) 2006 - 2012, Best Served Cold Media Limited.
 * @license		http://bestservedcold.com/license.html
 * @link		http://bestservedcold.com
 * @since		Version 1.0
 * @filesource
 */

// ------------------------------------------------------------------------

/**
 * Single Query Assets Library
 *
 * The Asset library simply checks the asset database and returns the asset location.  If the asset
 * is not in the database, then it calls the asset library and queues the asset for processing.
 *
 * @package		Asset Manager
 * @subpackage	Assets
 * @category	Libraries
 * @author  	Adam Lewis <adam.lewis@bestservedcold.com>
 * @since		2012-05-22
 */
require_once(APPPATH.'libraries/asset_manager/Am_single_query.php');
class Am_single_query_output extends Am_single_query{

	/**
	 * $output
	 *
	 * Page output for returning once manipulated by the library
	 *
	 * @access	public //TODO why is this public?  Should be protected for later classes possibly?
	 * @var		string
	 */
	public $output		= '';

	public function __construct()
	{
		$this->CI =& get_instance();
		$this->output = $this->CI->output->get_output();
	}

	/**
	 * Process Output
	 *
	 * Using the output provided, most probably in a MY_Output class, we shall go through the files and id's that are
	 * set in the views or controllers/models and replace the unique identifiers with the correct db entry, or process
	 * the entries by getting the DB entries or pushing them out to be processed by the handler to the CDN/base64
	 *
	 * @access	public
	 * @since	2012-06-08
	 * @param	string	$output
	 * @return	string	$output
	 */
	public function process_output()
	{
		// If the single query config setting is on, let's go for it
		if (! $this->CI->config->item('query', 'asset_manager') == 'single')
		{
			// It's off, just return the output, don't do anything here
			return $this->output;
		}

		// Get the existing Single Query object
		$object = $this->CI->load->is_loaded('am_single_query');

		// Check the object exists, and that it has asset information
		if (isset($this->CI->$object) && is_object($this->CI->$object) && ! empty($this->CI->$object->_page_assets))
		{
			// Use the object to create a database result object for processing
			$db_assets = $this->get_db_assets($this->CI->$object->_page_assets);

			// Now, cross reference the page_assets to the database assets, see what's left over so we can process those
			$left_overs = $db_assets->num_rows ? $this->replace_output($this->CI->$object->_page_assets,
				$db_assets->result_array()) : $this->CI->$object->_page_assets;

			// If we have any left_overs, let's process them and get them in the db
			$left_overs ? $this->eat_leftovers($left_overs) : NULL;
		}
		else
		{
			// Oops, there is no single query object?
			log_message('error', "[ASSETS] There is no single_query object to reference");
		}
		return $this->output;
	}

	/**
	 * Get DB Assets
	 *
	 * Gets the assests from the database, doesn't need much description.
	 *
	 * @access	private
	 * @since	2012-06-08
	 * @param	object					$page_assets
	 * @return	CI_DB_active_record
	 */
	private function get_db_assets($page_assets)
	{
		// Start a query, we'll need the id, base64, cdn, file and group from the assets table
		$this->CI->db->select('id, base64, cdn, file, group, filesize, filetype')
			->from($this->CI->config->item('asset_table', 'asset_manager'));

		// Go through the assets and build up a nice query
		foreach ($page_assets as $asset)
		{
			is_array($asset) ? $this->CI->db->or_where("(`file` = '${asset[0]}'".($asset[1] ? " AND `group` = ".
				"${asset[1]}" : NULL ). ")") : $ids[] = $asset;
		}

		// If there are some ids, this is the most efficient query, what we want users to do ideally, do a where in
		isset($ids) ? $this->CI->db->where_in('id', $ids) : NULL;

		// return the database object
		return $this->CI->db->get();
	}

	/**
	 * Eat Leftovers
	 *
	 * Takes all the leftover assets that aren't in the database and tries to process them through
	 *
	 * @access	private
	 * @since	2012-06-08
	 * @param	array		$assets
	 * @reutrn	void
	 */
	private function eat_leftovers($leftovers)
	{
		// go through the left over assets that aren't in the database and process them
		foreach ($leftovers as $key => $leftover)
		{
			// Send the two bits of information to the parent handler
			if ($data = $this->process_asset($leftover[0], $leftover[1]))
			{
				// Now update the output with the asset data and the key
				$this->update_output($data, $key);
			}
			else
			{
				// Oh no!  Something's gone wrong there, let's just process with the original data for now
				log_message('error', "[ASSETS SINGLE_QUERY_OUTPUT] Processing the asset has failed");
				$this->update_output($leftover[0], $key);
			}
		}
	}

	/**
	 * Replace output
	 *
	 * Cross references the page assets against the database assets.  If the asset is in the database, it will process
	 * that and remove it from the array ready to return to the eat leftovers function.
	 *
	 * @access	private
	 * @since	2012-06-08
	 * @param	array		$page_assets
	 * @param	object		$db_assets
	 * @return	object
	 */
	private function replace_output($page_assets, $db_assets)
	{
		// Go through each of the params detailed in $object and see if they're in the database object
		foreach ($page_assets as $page_asset_key => $page_asset_data)
		{
			foreach ($db_assets as $db_asset)
			{
				// Is the $page_asset_data an array AND the database record file equals the page_asset_data OR
				// if the $page_asset_data is an integer and the page asset id matches the database id, then carry on
				if ((is_array($page_asset_data) && $db_asset['file'] === $page_asset_data[0] &&
						$db_asset['group'] == $page_asset_data[1]) ||
					(is_int($page_asset_data) && $db_asset['id ']=== $page_asset_data))
				{
					// We need to check each asset to make sure it doesn't need updating...
					$db_asset = $this->check_asset($db_asset);

					// Replace the unique key with the database data in the output
					$this->update_output($db_asset, $page_asset_key);

					// Now take it out of the object, should make things faster
					unset($page_assets[$page_asset_key]);
				}
			}
		}
		// Return what's left
		return $page_assets;
	}

	/**
	 * Update Output
	 *
	 * Takes the asset and updates the output depending upon what state the asset is in.  If it's a base64, it will
	 * use that, if it's a cdn file, it will turn it into a cdn_url and use that, otherwise, it'll just use the
	 * original file
	 *
	 * @access	private
	 * @since	2012-06-08
	 * @param	array		$data
	 * @param	string		$key
	 * @return	void
	 */
	private function update_output($asset, $key)
	{
		// Read the asset information and work out what we're doing with it...
		$src = $this->single_asset_src($asset);

		// How did we get on?
		if (! $src)
		{
			// Bugger, somethings gone wrong...
			log_message('error', "[ASSETS SINGLE_QUERY] There is something wrong with the asset data passed to ".
				"udpate_output key: [$key], data is [".var_export($asset, TRUE)."]");
			$src = '';
		}
		// search and replace the output
		$this->output = preg_replace('/'.$key.'/', $src, $this->output);
	}

}
